from datetime import datetime, timedelta, timezone 
from typing import Annotated 

from jose import jwt , JWTError
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from passlib.hash import sha256_crypt 
from config.settings import get_settings 
from data.user import get_one , get_one_all
from model.token import Token, TokenData 
from model.user import User, UserInDB 
from error import Missing 


settings = get_settings()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='token') 


def authenticate_user(username: str, password: str) -> UserInDB | None : 
    try: 
        user = get_one_all(username) 
        
    except Missing: 
        return None 
    if not sha256_crypt.verify(password, user.hashed_password): 
        return None 
    
    return user 
    
    
def create_access_token(data:dict, expires_delta: timedelta | None = None) -> str: 
    to_encode = data.copy() 
    if expires_delta: 
        expire = datetime.now(timezone.utc) + expires_delta 
    else: 
        expire = datetime.now(timezone.utc) + timedelta(minutes=15) 
    
    to_encode['exp'] = expire 
    encode_jwt = jwt.encode(to_encode, settings.secret_key, settings.algorithm)

    return encode_jwt 


async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> User: 
    credentials_exception = HTTPException(
        status_code=401, 
        detail='Could not validate credentials', 
        headers={'WWW-Authenticate': 'Bearer'} 
    )

    try: 
        payload = jwt.decode(token, settings.secret_key, settings.algorithm) 
        username = payload.get('sub')
        if not username: 
            raise credentials_exception
    except JWTError: 
        raise credentials_exception 
    
    try:
        user = get_one(username) 
    except Missing:
        raise credentials_exception 
    
    return user 


async def get_current_active_user(current_user: Annotated[User, Depends(get_current_user)]) -> User: 
    if current_user.disabled: 
        raise HTTPException(status_code=400, detail='Inactive user')
    
    return current_user 



